/**
 * @file STL_TcpServer.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2021-02-08
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2021
 * 
 */

#include "STL_TcpServer.h"
#include "STL_Socket.h"

namespace STL
{
    STL_TcpServer::STL_TcpServer(TcpServerConfig config) : m_config(config), m_listener_socket(-1), m_epoll_fd(-1), m_run(false)
    {
    }

    STL_TcpServer::~STL_TcpServer()
    {

        if (m_listener_socket != -1)
        {
            STL_SAFE_CLOSE(m_listener_socket);
        }
        if (m_epoll_fd != -1)
        {
            STL_SAFE_CLOSE(m_epoll_fd);
        }
    }

    void STL_TcpServer::startListen()
    {
        m_run = true;
        setStatus(STL_TcpServer::Status::Init);
        m_thread = std::thread(std::bind(&STL_TcpServer::tcpThread, this));
    }

    void STL_TcpServer::stopListen()
    {
        m_run = false;
        setStatus(STL_TcpServer::Status::Init);
    }

    int STL_TcpServer::onData(int socket)
    {
        // stlog_trace("STL_TcpServer::onData\n");
        if (m_socketMap.count(socket) != 1)
        {
            stlog_warn("Invalid socket[%d]\n", socket);
            return -1;
        }

        size_t buflen;
        long sizeRet = recv(socket, m_buffer, STL_TcpServer_Buffer_Max, 0);
        int ierrno = errno;
        if (sizeRet == 0 || (sizeRet < 0 && (ierrno != EAGAIN && ierrno != EWOULDBLOCK && ierrno != EINTR)))
        {
            // perror("socket recv"); // 断开连接不属于错误逻辑
            // stlog_error("sizeRet:%ld---ierrno:%d \n", sizeRet, ierrno);// 断开连接不属于错误逻辑
            return -1;
        }
        std::shared_ptr<std::string> sp_data(new std::string((char *)m_buffer, (size_t)sizeRet));
        stlog_trace("STL_TcpServer::recvData:\n%s\n", sp_data->c_str());
        if (m_DataCallback)
            m_DataCallback(socket, sp_data);
        return 0;
    }

    void STL_TcpServer::onConnect(int socket, std::string ip, uint16_t port)
    {
        // stlog_trace("STL_TcpServer::onConnect\n");
        if (m_socketMap.count(socket) == 1)
        {
            stlog_warn("ERROR, Invalid socket");
            onDisconnect(socket); // 假设socket存在,删除已存在的socket,（正常情况此调用不会无效）
        }

        // stlog_trace("input [SOCKET: %d, IP：%s,  PORT：%d]\n", socket, ip.c_str(), port);
        m_socketMap[socket] = std::shared_ptr<SocketInfo>(new SocketInfo(socket, ip, port));
        if (m_ConnectCallback)
            m_ConnectCallback(socket, ip, port);
    }
    void STL_TcpServer::onDisconnect(int socket)
    {
        static std::mutex removeSocketMutex;
        if (removeSocketMutex.try_lock()) //TODO 不靠谱，聊胜于无，后续修改
        {
            // stlog_trace("STL_TcpServer::onDisconnect\n");
            if (m_socketMap.count(socket) == 1)
            {
                m_socketMap.erase(socket);
                if (m_DisconnectCallback)
                    m_DisconnectCallback(socket);
            }
            removeSocketMutex.unlock();
        }
    }

    void STL_TcpServer::sendData(int socket, std::string &data)
    {
        // stlog_trace("STL_TcpServer::sendData\n%s\n", data.c_str());
        if (m_socketMap.count(socket) == 1)
        {
            //TODO 发送参考haicams项目,
            send(socket, data.data(), data.size(), 0);
        }
    }

    void STL_TcpServer::closeSocket(int socket)
    {
        STL_Socket::removeConnectionSocketFromEpoll(m_epoll_fd, socket);
        STL_SAFE_CLOSE(socket);
        onDisconnect(socket);
    }

    void STL_TcpServer::tcpThread()
    {
        /*创建listener_scoket*/
        m_listener_socket = STL_Socket::createTcpListenerSocket();
        // stlog_info("创建listener_scoket => tcpThread::m_listener_socket = [%d]", m_listener_socket);
        if (m_listener_socket < 0)
        {
            //TODO 错误处理
        }
        /*绑定*/
        STL_Socket::bindListenerSocket(m_listener_socket, m_config.ip, m_config.port);
        // stlog_info("tcpThread::bindListenerSocket [%d]", m_listener_socket);

        /*监听*/
        STL_Socket::listenListenerSocket(m_listener_socket);
        // stlog_info("tcpThread::listenListenerSocket [%d]", m_listener_socket);

        /*创建epoll_fd*/
        m_epoll_fd = STL_Socket::createEpoll();
        // stlog_info("创建epoll_fd => tcpThread::m_epoll_fd = [%d]", m_epoll_fd);
        if (m_epoll_fd < 0)
        {
            //TODO 错误处理
        }
        /*添加listen_socket*/
        STL_Socket::addListenerSocketToEpoll(m_epoll_fd, m_listener_socket);
        // stlog_info("创建epoll_fd => tcpThread::m_epoll_fd = [%d] m_listener_socket = [%d]", m_epoll_fd, m_listener_socket);
        do
        {
            auto list = STL_Socket::getEpollEvent(m_epoll_fd); //  阻塞函数
            for (int i = 0; i < list.size(); i++)
            {
                if (list.at(i) == m_listener_socket)
                {

                    stlog_info("tcpThread::m_listener_socket Event = [%d]", m_listener_socket);
                    auto info = STL_Socket::acceptNewConnection(m_listener_socket);

                    int conn_sock = info.socket;
                    stlog_info("tcpThread::conn_sock Connected = [%d]", conn_sock);
                    STL_Socket::addConnectionSocketToEpoll(m_epoll_fd, info.socket);
                    stlog_info("tcpThread::conn_sock AddEpoll =m_epoll_fd = [%d], conn_sock = [%d]", m_epoll_fd, conn_sock);
                    onConnect(info.socket, info.ip, info.port);
                }
                else
                {
                    int conn_sock = list.at(i);
                    stlog_info("tcpThread::conn_sock Event = [%d]", conn_sock);
                    int ret = onData(conn_sock);
                    if (ret < 0)
                    {
                        STL_Socket::removeConnectionSocketFromEpoll(m_epoll_fd, conn_sock);
                        closeSocket(conn_sock);
                    }
                }
            }
        } while (m_run);
    }

} // namespace STL